package com.engine.jucailinkq.attendance.attendanceanalysis.cmd.workovertime;

import com.engine.jucailinkq.attendance.attendanceanalysis.dto.clockpoint.ClockPointDTO;
import com.engine.jucailinkq.attendance.enums.*;
import com.engine.common.biz.AbstractCommonCommand;
import com.engine.common.entity.BizLogContext;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.jucailinkq.common.util.Utils;
import com.engine.core.interceptor.CommandContext;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import weaver.general.Util;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 根据加班时间计算获得加班时长
 */
@Slf4j
public class GetOvertimeDurationCmd extends AbstractCommonCommand<Map<String, Object>> {
    public GetOvertimeDurationCmd(Map<String, Object> params) {
        this.params = params;
    }

    @Override
    public BizLogContext getLogContext() {
        return null;
    }

    @Override
    public Map<String, Object> execute(CommandContext commandContext) {
        Map<String, Object> resultMap = Maps.newHashMap();

        //考勤项目
        List<Map<String,Object>> attendanceItems = (List<Map<String,Object>>)params.get("attendanceItems");
        //排班
        List<Map<String, Object>> scheduleResult = (List<Map<String,Object>>)params.get("scheduleResult");
        //分析日期
        String analysisDate = Util.null2String(params.get("analysisDate"));
        //请假记录
        List<Map<String,Object>> askForLeaveList = (List<Map<String,Object>>)params.get("askForLeaveList");
        //请假项目
        Map<String,Map<String,Object>> askForLeaveItems = (Map<String,Map<String,Object>>)params.get("askForLeaveItems");
        //外出记录
        List<Map<String,Object>> evectionList = (List<Map<String,Object>>)params.get("evectionList");
        //外出项目
        Map<String,Map<String,Object>> evectionItems = (Map<String,Map<String,Object>>)params.get("evectionItems");
        //请假后消除的异常
        List<Map<String,Object>> offsetAskForLeaveAnomaly = (List<Map<String,Object>>)params.get("offsetAskForLeaveAnomaly");
        //出差外出后消除的异常
        List<Map<String,Object>> offsetEvectionAnomaly = (List<Map<String,Object>>)params.get("offsetEvectionAnomaly");
        //请假-抵消异常
        Map<Map<String, Object>,List<Map<String, Object>>> askForLeaveToOffsetAbnomaly = (Map<Map<String, Object>,List<Map<String, Object>>>)params.get("askForLeaveToOffsetAbnomaly");
        //出差-抵消异常
        Map<Map<String, Object>,List<Map<String, Object>>> evectionToOffsetAbnomaly = (Map<Map<String, Object>,List<Map<String, Object>>>)params.get("evectionToOffsetAbnomaly");
        //经过请假外出处理过的异常项目
        List<Map<String,Object>> abnormalClockInList = (List<Map<String,Object>>)params.get("abnormalClockInList");
        //加班项目
        List<Map<String, Object>> workTimeBeLateItems = (List<Map<String,Object>>)params.get("workTimeBeLateItems");
        //打卡匹配
        Map<String,Map<String,Object>> clcokInTimeMap = (Map<String,Map<String,Object>>)params.get("clcokInTimeMap");
        //班次
        Map<String, Object> scheduleMap = (Map<String, Object>)params.get("scheduleMap");
        //打卡卡点
        List<ClockPointDTO> clcokInTimeDataList = (List<ClockPointDTO>)params.get("clcokInTimeData");




        //总抵消异常
        List<Map<String,Object>> anomalyList = Lists.newArrayList();
        anomalyList.addAll(offsetEvectionAnomaly);
        anomalyList.addAll(offsetAskForLeaveAnomaly);


        String bdlx = Util.null2String(scheduleMap.get("bdlx"));
        Map<String, Object> getWorkOverTimeParam = Maps.newHashMap();
        //加班开始时间
        String kssj = Utils.getkssjTime(scheduleMap, analysisDate);
        List<ClockPointDTO> startClockPointDTO = clcokInTimeDataList.stream().filter(e->e.getPointType() == ClockPointEnum.START &&
                DateUtil.getTime(kssj).compareTo(DateUtil.getTime(e.getClassTime())) == 0).collect(Collectors.toList());
        //加班结束时间
        String jssj = Utils.getjssjTime(scheduleMap, analysisDate);
        List<ClockPointDTO> endClockPointDTO = clcokInTimeDataList.stream().filter(e->e.getPointType() == ClockPointEnum.END &&
                DateUtil.getTime(jssj).compareTo(DateUtil.getTime(e.getClassTime())) == 0).collect(Collectors.toList());
        //加班实际开始时间
        String realityStartTime = "";
        //加班实际结束时间
        String realityEndime = "";


        getWorkOverTimeParam.put("attendanceItems", attendanceItems);
        getWorkOverTimeParam.put("rqlx", scheduleMap.get("rqlx"));

        //开始必须打卡，当为加班计划类型的加班时已考勤项目设置为准
        String ksjbbxydk = scheduleMap.get("ksdk").toString();
        //结束必须打卡，当为加班计划类型的加班时已考勤项目设置为准
        String jsjbbxydk = scheduleMap.get("jsdk").toString();


        double jbsc = Double.valueOf(Util.null2String(scheduleMap.get("edxss")));

        //提前打卡开始的时长计入加班
        String tqdkjrjb = Util.null2String(workTimeBeLateItems.get(0).get("tqdkjrjb"));
        //推后打卡结束的时长计入加班
        String thdkjrjb = Util.null2String(workTimeBeLateItems.get(0).get("thdkjrjb"));
        //结算加班时长不得超过申请的时长
        String jbscbdccsqsc = Util.null2String(workTimeBeLateItems.get(0).get("jbscbdccsqsc"));
        //是否扣除时间区间内的就餐休息时长
        String zdkcjcxxsc = Util.null2String(workTimeBeLateItems.get(0).get("zdkcjcxxsc"));
        //晚于开始时间忽略分钟数
        String jbwdhlfzs = Util.null2String(workTimeBeLateItems.get(0).get("jbwdhlfzs")).equals("") ? "0" : Util.null2String(workTimeBeLateItems.get(0).get("jbwdhlfzs"));
        //早于结束时间忽略分钟数
        String jbzzhlfzs = Util.null2String(workTimeBeLateItems.get(0).get("jbzzhlfzs")).equals("") ? "0" : Util.null2String(workTimeBeLateItems.get(0).get("jbzzhlfzs"));

        /**
         * 获得实际加班时间
         */
        if (CheckBoxEnum.CHECKED.getKey().equals(ksjbbxydk)) {
            Map<String, Object> clcokInTimeData = clcokInTimeMap.get(kssj);
            if (clcokInTimeData == null) {
                log.debug("加班开始时间漏卡: {},clcokInTimeData:{}", kssj, clcokInTimeMap);
                resultMap.put("jbsc","0");
                return resultMap;
            }
            String hourTime = clcokInTimeData.get("signtime").toString();
            if (hourTime.length() > 5) {
                hourTime = hourTime.substring(0, 5);
            }
            String signTime = clcokInTimeData.get("signdate") + " " + hourTime;
            realityStartTime = signTime;
        } else {
            // 如果上一班段是工作时段，且开始必须打卡且有卡，否则不算加班
            int indexTemp = scheduleResult.indexOf(scheduleMap);
            if (indexTemp > 0) {
                Map<String, Object> previousScheduleMap = scheduleResult.get(indexTemp - 1);
                if ((!CollectionUtils.isEmpty(previousScheduleMap)) && previousScheduleMap.get("bdlx").equals(ClassSegmentTypeEnum.WORK_TIME.getKey())
                       && CheckBoxEnum.CHECKED.getKey().equals(previousScheduleMap.get("ksdk").toString())) {
                    String previousKssj = Utils.getkssjTime(previousScheduleMap, analysisDate);
                    Map<String, Object> clcokInTimeDataTemp = clcokInTimeMap.get(previousKssj);
                    if (clcokInTimeDataTemp == null) {
                        log.debug("加班开始时间漏卡: {},clcokInTimeData:{}", previousKssj, clcokInTimeDataTemp);
                        resultMap.put("jbsc","0");
                        return resultMap;
                    } else {
                        realityStartTime = kssj;
                    }
                } else {
                    realityStartTime = kssj;
                }
            } else {
                realityStartTime = kssj;
            }

//            realityStartTime = kssj;
        }
        if (CheckBoxEnum.CHECKED.getKey().equals(jsjbbxydk)) {
            Map<String, Object> clcokInTimeData = clcokInTimeMap.get(jssj);
            if (clcokInTimeData == null) {
                log.debug("加班结束时间漏卡: {},clcokInTimeData:{}", jssj, clcokInTimeData);
                resultMap.put("jbsc","0");
                return resultMap;
            }
            String hourTime = clcokInTimeData.get("signtime").toString();
            if (hourTime.length() > 5) {
                hourTime = hourTime.substring(0, 5);
            }
            String signTime = clcokInTimeData.get("signdate") + " " + hourTime;
            realityEndime = signTime;
        } else {
            realityEndime = jssj;
        }
        jbsc = jbsc * 60;
        if (CheckBoxEnum.CHECKED.getKey().equals(tqdkjrjb) && CheckBoxEnum.CHECKED.getKey().equals(ksjbbxydk)) {
            //提前打卡开始的时长计入加班
            if (DateUtil.getTime(realityStartTime).compareTo(DateUtil.getTime(kssj)) < 0) {
                jbsc += DateUtil.getBetWeenMinutes(realityStartTime, kssj);
            }
        }
        if (CheckBoxEnum.CHECKED.getKey().equals(thdkjrjb) && CheckBoxEnum.CHECKED.getKey().equals(jsjbbxydk)) {
            // 推后打卡结束的时长计入加班
            if (DateUtil.getTime(realityEndime).compareTo(DateUtil.getTime(jssj)) > 0) {
                jbsc += DateUtil.getBetWeenMinutes(jssj, realityEndime);
            }
        }

        /**
         * 当存在迟到项目时扣除异常时长，不存在时扣除实际迟到时间
         */
        List<Map<String, Object>> beLateAbnormal = abnormalClockInList.stream().filter(e -> e.get("pointTime").equals(kssj)).collect(Collectors.toList());
        double beLateTime = 0;
        if (beLateAbnormal.size() > 0) {
            String hsdw = Util.null2String(beLateAbnormal.get(0).get("hsdw"));
            String itemduration = Util.null2String(beLateAbnormal.get(0).get("itemduration"));
            AttendanceItemTypeEnum itemType = (AttendanceItemTypeEnum) beLateAbnormal.get(0).get("itemType");
            if (itemType == AttendanceItemTypeEnum.MISSE_CARD) {
                //早上漏卡
                beLateTime = jbsc;
            } else {
                if (AccountingUnitEnum.DAY.getKey().equals(hsdw)) {
                    beLateTime = Integer.valueOf(scheduleMap.get("edsc").toString()) * Double.valueOf(itemduration);
                } else if (AccountingUnitEnum.HOUR.getKey().equals(hsdw)) {
                    beLateTime = Double.valueOf(itemduration) * 60;
                } else if (AccountingUnitEnum.MINUTES.getKey().equals(hsdw)) {
                    beLateTime = Double.valueOf(itemduration);
                } else if (AccountingUnitEnum.ONCE.getKey().equals(hsdw)) {
                    beLateTime = jbsc;
                }
            }
        } else {
            String kssjTime = kssj;
            if (startClockPointDTO.size()>0 && startClockPointDTO.get(0).getElasticTime() != null && !startClockPointDTO.get(0).getElasticTime().equals("")){
                kssjTime = startClockPointDTO.get(0).getElasticTime();
            }
            if (DateUtil.getTime(realityStartTime).compareTo(DateUtil.getTime(kssjTime)) > 0) {
                beLateTime = DateUtil.getBetWeenMinutes(kssjTime, realityStartTime);
                List<Map<String, Object>> collect1 = anomalyList.stream().filter(e -> e.get("pointTime").equals(kssj)).collect(Collectors.toList());

                if (!"".equals(jbwdhlfzs) && beLateTime < Integer.valueOf(jbwdhlfzs)) {
                    beLateTime = 0;
                }
                //当存在迟到异常被抵消的情况
                if (collect1.size() > 0) {
                    beLateTime = 0;
                }

            }
        }
        /**
         * 当存在早退项目时扣除异常时长,不存在时扣除实际早退时间
         */
        List<Map<String, Object>> leaveEarlyAbnormal = abnormalClockInList.stream().filter(e -> e.get("pointTime").equals(jssj)).collect(Collectors.toList());
        double leaveElaryTime = 0;
        if (leaveEarlyAbnormal.size() > 0) {
            String hsdw = Util.null2String(leaveEarlyAbnormal.get(0).get("hsdw"));
            String itemduration = Util.null2String(leaveEarlyAbnormal.get(0).get("itemduration"));
            AttendanceItemTypeEnum itemType = (AttendanceItemTypeEnum) leaveEarlyAbnormal.get(0).get("itemType");
            if (itemType == AttendanceItemTypeEnum.MISSE_CARD) {
                //下午漏卡
                leaveElaryTime = jbsc;

            } else {
                if (AccountingUnitEnum.DAY.getKey().equals(hsdw)) {
                    leaveElaryTime = Integer.valueOf(scheduleMap.get("edsc").toString()) * Double.valueOf(itemduration);
                } else if (AccountingUnitEnum.HOUR.getKey().equals(hsdw)) {
                    leaveElaryTime = Double.valueOf(itemduration) * 60;
                } else if (AccountingUnitEnum.MINUTES.getKey().equals(hsdw)) {
                    leaveElaryTime = Double.valueOf(itemduration);
                } else if (AccountingUnitEnum.ONCE.getKey().equals(hsdw)) {
                    leaveElaryTime = jbsc;
                }
            }
        } else {
            String jssjTime = jssj;

            if (endClockPointDTO.size()>0 && endClockPointDTO.get(0).getElasticTime() != null && !endClockPointDTO.get(0).getElasticTime().equals("")){
                jssjTime = endClockPointDTO.get(0).getElasticTime();
            }
            if (DateUtil.getTime(realityEndime).compareTo(DateUtil.getTime(jssjTime)) < 0) {
                leaveElaryTime = DateUtil.getBetWeenMinutes(realityEndime, jssjTime);
                List<Map<String, Object>> collect2 = anomalyList.stream().filter(e -> e.get("pointTime").equals(jssj)).collect(Collectors.toList());
                if (!"".equals(jbzzhlfzs) && leaveElaryTime < Integer.valueOf(jbzzhlfzs)) {
                    leaveElaryTime = 0;
                }
                //当存在早退异常被抵消的情况
                if (collect2.size() > 0) {
                    leaveElaryTime = 0;
                }
            }

        }

        /**
         * 当存在请假时，考虑扣除请假时长
         */
        int askForLeaveTime = removeAskForLeave(scheduleMap,realityStartTime, realityEndime
                , bdlx, askForLeaveList, askForLeaveItems, askForLeaveToOffsetAbnomaly);

        /**
         * 当存在外出时，考虑扣除外出时长
         */
        int evectionTime = removeEvection(scheduleMap,realityStartTime, realityEndime
                , bdlx, evectionList, evectionItems, evectionToOffsetAbnomaly);

        /**
         * 加班时段所包含的就餐时段
         */
        int dinnerTime = 0;
        double workflowBetweenHour = Double.valueOf(String.format("%.2f", DateUtil.getBetWeenMinutes(kssj,jssj) / 60.0));
        if (workflowBetweenHour <=  Double.valueOf(Util.null2String(scheduleMap.get("edxss"))) && scheduleMap.get("bdlx").equals(ClassSegmentTypeEnum.OVERTIME_PLAN.getKey())){
            //当为加班计划时，前台流程未扣过休息时长
            dinnerTime = Utils.dinnerTime(realityStartTime, realityEndime, scheduleResult, analysisDate);
        }

        log.debug("未扣减过的加班时长: {}", jbsc);
        log.debug("beLateTime :[{}],leaveElaryTime :[{}],askForLeaveTime:[{}],evectionTime:[{}],dinnerTime:[{}]", beLateTime, leaveElaryTime, askForLeaveTime, evectionTime, dinnerTime);
        BigDecimal jbscbig = new BigDecimal(jbsc);
        if (CheckBoxEnum.CHECKED.getKey().equals(zdkcjcxxsc)){
            //扣除就餐时长
            jbsc = jbscbig.subtract(new BigDecimal(beLateTime)).subtract(new BigDecimal(leaveElaryTime)).subtract(new BigDecimal(askForLeaveTime)).subtract(new BigDecimal(evectionTime)).subtract(new BigDecimal(dinnerTime)).intValue();
        }else {
            jbsc = jbscbig.subtract(new BigDecimal(beLateTime)).subtract(new BigDecimal(leaveElaryTime)).subtract(new BigDecimal(askForLeaveTime)).subtract(new BigDecimal(evectionTime)).intValue();
        }
        if (CheckBoxEnum.CHECKED.getKey().equals(jbscbdccsqsc)) {
            if (jbsc > Double.valueOf(Util.null2String(scheduleMap.get("edxss"))) * 60) {
                jbsc = Double.valueOf(Util.null2String(scheduleMap.get("edxss"))) * 60;
            }
        }
        log.debug("扣减过的加班时长 :[{}]", jbsc);
        resultMap.put("jbsc",jbsc);
        resultMap.put("realityStartTime",realityStartTime);
        resultMap.put("realityEndime",realityEndime);

        return resultMap;
    }

    /**
     * 请假影响加班时长
     * @param kssj 加班开始时间
     * @param jssj 加班结束时间
     * @param askForLeaveList 请假集合
     * @param askForLeaveItems 请假项目
     * @return 请假在加班中所占时间
     */
    public int removeAskForLeave(Map<String, Object> scheduleMap,String kssj,String jssj,String bdlx,List<Map<String, Object>> askForLeaveList,
                                 Map<String,Map<String,Object>> askForLeaveItems,Map<Map<String, Object>,List<Map<String, Object>>> askForLeaveToOffsetAbnomaly ){
        int employTime = 0;
        //半天规则
        String btgz = Util.null2String(scheduleMap.get("btgz"));

        double edsc = Double.valueOf(scheduleMap.get("edsc").toString());

        for (int i=0;i<askForLeaveList.size();i++){
            //请假时长
            String qjsc = Util.null2String(askForLeaveList.get(i).get("qjsc"));
            //请假项目
            Map<String,Object> askForLeaveItem = askForLeaveItems.get(askForLeaveList.get(i).get("jqlx"));
            //作用时段
            String zysd = Util.null2String(askForLeaveItem.get("zysd"));
            //消除异常
            List<Map<String, Object>> abnormalList = askForLeaveToOffsetAbnomaly.get(askForLeaveList.get(i));
            //半天请假
            String btj = Util.null2String(askForLeaveList.get(i).get("btj"));

            if (!zysd.contains(Utils.getWorkFor(bdlx)) && !zysd.contains(WorkForTimeEnum.ALL_TIME.getKey())){
                continue;
            }
            List<Map<String, Object>> list = Lists.newArrayList();
            if (abnormalList != null && abnormalList.size() > 0){
                list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalList,Util.null2String(params.get("analysisDate")));
            }

            if ("".equals(Util.null2String(askForLeaveList.get(i).get("kssj"))) || "".equals(Util.null2String(askForLeaveList.get(i).get("jssj")))){
                if (CheckBoxEnum.CHECKED.getKey().equals(btj) && (btgz.equals(HalfDayRuleREnum.BY_CLASS_SET.getKey()) || btgz.equals(HalfDayRuleREnum.FIXED_DURATION.getKey()))){
                    //半天请假
                    if (list.size() > 0){
                        employTime+=Double.valueOf(scheduleMap.get("edfzs").toString());
                    }else if (zysd.equals(Utils.getWorkFor(bdlx))){
                        employTime+=Double.valueOf(scheduleMap.get("edfzs").toString());
                    }
                } else if (!"".equals(qjsc)){
                    //时长请假
                    if (abnormalList != null && abnormalList.size() > 0){
                        employTime += Math.round(list.size()/Double.valueOf(abnormalList.size()) *Double.valueOf(qjsc)*60);
                    }else if (zysd.equals(Utils.getWorkFor(bdlx))){
                        employTime = Double.valueOf(Double.valueOf(qjsc)*60).intValue();
                    }
                }
            }else {
                //按照开始时间，结束时间请假
                employTime +=Utils.getStartAndEndTime(kssj,jssj,askForLeaveList.get(i));
            }
        }
        return employTime;
    }

    /**
     * 外出影响加班时长
     * @param kssj 加班开始时间
     * @param jssj 加班结束时间
     * @param evectionList 外出集合
     * @return
     */
    public int removeEvection(Map<String, Object> scheduleMap,String kssj,String jssj,String bdlx,List<Map<String, Object>> evectionList,Map<String,
            Map<String,Object>> evectionItems,Map<Map<String, Object>,List<Map<String, Object>>> evectionToOffsetAbnomaly){
        int employTime=0;
        //半天规则
        String btgz = Util.null2String(scheduleMap.get("btgz"));

        for (int i=0;i<evectionList.size();i++){
            //出差时长
            String ccsc = Util.null2String(evectionList.get(i).get("ccsc"));
            //出差项目
            Map<String,Object> evectionItem = evectionItems.get(evectionList.get(i).get("cclx"));
            //作用时段
            String zysd = Util.null2String(evectionItem.get("zysd"));
            //消除异常
            List<Map<String, Object>> abnormalList = evectionToOffsetAbnomaly.get(evectionList.get(i));
            //半天出差
            String btcc = Util.null2String(evectionItem.get("btcc"));

            if (!zysd.contains(Utils.getWorkFor(bdlx)) && !WorkForTimeEnum.ALL_TIME.getKey().equals(zysd)){
                continue;
            }
            List<Map<String, Object>> list = Lists.newArrayList();
            if (abnormalList != null && abnormalList.size() > 0){
                list = Utils.getAbnormalListBySchedule(scheduleMap,abnormalList,Util.null2String(params.get("analysisDate")));
            }

            if ("".equals(Util.null2String(evectionList.get(i).get("kssj"))) || "".equals(Util.null2String(evectionList.get(i).get("jssj")))){
                if (CheckBoxEnum.CHECKED.getKey().equals(btcc) && (btgz.equals(HalfDayRuleREnum.BY_CLASS_SET.getKey()) || btgz.equals(HalfDayRuleREnum.FIXED_DURATION.getKey()))){
                    //半天出差
                    if (list.size() > 0){
                        employTime+=Double.valueOf(scheduleMap.get("edfzs").toString());
                    }else if (zysd.equals(Utils.getWorkFor(bdlx))){
                        employTime+=Double.valueOf(scheduleMap.get("edfzs").toString());
                    }
                } else if (!"".equals(ccsc)){
                    //时长请假
                    if (abnormalList != null && abnormalList.size() > 0){
                        employTime += Math.round(list.size()/Double.valueOf(abnormalList.size()) *Double.valueOf(ccsc)*60);
                    }else if (zysd.equals(Utils.getWorkFor(bdlx))){
                        employTime = Double.valueOf(Double.valueOf(ccsc)*60).intValue();
                    }
                }
            }else {
                //按照开始时间，结束时间请假
                employTime +=Utils.getStartAndEndTime(kssj,jssj,evectionList.get(i));
            }
        }
        return employTime;
    }


}
